在 React 项目中使用 React-intl 实现多语言支持

前言

最近在项目中添加了语言国际化的功能。

语言国际化,也有人说成是语言本地化,其实就是为Web App添加多语言,我们的项目当前包含了中文版和英文版,按理来说『逐字替换』也不是多大事儿,但是,这么Low的做法,有钱途吗?

一开始的时候,我考虑的是传统的为整个项目添加config文件,根据不同的语言和地区,加载不同的config文件,就能够达到界面语言切换的目的。当然,也正是因为这个想法太过于幼稚,所以才被称为『一开始』的想法。语言的国际化不仅仅是将界面上的UI文字翻译成另一种语言,还包括了日期&时间显示,数字显示(英文环境下每隔3位一个逗号:1,000),量词的显示(一个苹果是apple,两个苹果就应该是apples),甚至还有一个字符串中间插了一个变量的情况(”今天午饭吃了{count}个鸡腿”)…

image

所以这并不只是一个简单的字符替换问题,并且,我们要很方便的导出一个目录,放到word或者page当中,给到其他同事对照着进行翻译工作,这个非常重要!!难道你要让产品经理直接在代码里改么?或者你想一个一个搜索替换?不考虑清楚就干的话,相信我,You’ll pay for this。

作为一个有追求的代码家,你肯定不希望在index.html当中增加一行Script引用吧?另外,UI中的文字全部都是使用图片的那个同学,请起立,滚。如果想要在一个React项目中,优雅的import something from somewhere,然后将界面中的文字用<首字母大写 /> 组件替代,最后通过简单的配置实现语言的国际化,那我们就用React-intl吧。

image

React-intl

项目地址: https://github.com/yahoo/react-intl

1. 安装React-intl

假设你已经在你的系统中安装了node.js和npm,如果你还不知道这两个是什么东西,请自行百度,对,在百度都能找到答案。

打开终端,进入项目根目录,输入以下指令安装React-intl:

1
npm install react-intl -save

注意:为了兼容Safari各个版本,需要同时安装 intl,intl在大部分的『现代』浏览器中是默认自带的,但是Safari和IE11以下的版本就没有了,这里需要留个心眼。

安装intl需要在终端中输入以下指令:

1
npm install intl --save

这里还有一个注意:由于React-intl的每一个组件的使用方法大同小异,和ReactJS的语法完全一致,所以我就仅仅描述如何使用这个组件的用法,借此抛砖引玉,相信看完之后已经足够帮助你迅速的去使用这个开源框架了。

2. 引用React-intl

1
import { FormattedMessage } from 'react-intl';

由于我使用的是ES6的语法,所以是支持直接引用组件的

1
require ReactIntl from 'react-intl';

3. 创建locale配置文件

这里,我们将文件命名为zh_CN.js和en_US.js,代表中文和美式英语的配置包。

在zh_CN.js编写如下代码:

1
2
3
4
5
6
const zh_CN = {
hello:"你好,吴!"
superHello:"你好,{ someone } !"
}
export default zh_CN;
在en_US.js编写如下代码:
1
2
3
4
5
const en_US = {
hello:"Hello, w!"
superHello:"Hello, { someone } !"
}
export default en_US;

于是,我们就创建好了locale文件,但是,在实际的项目中配置文件不会这么简单,您可能需要根据业务需求按照不同的页面或者不同的功能块创建不同的文件树,然后用模块化的方法将不同的配置文件进行组织,已达成你的目标,这里我也就没能力逼逼太多了。

你可能想问,{ someone } 是什么鬼?其实悟性高一些的话就应该已经猜到,这个应该就是前面提到过的在字符串中插入动态参数的用法,事实上也是这样的。

4. 使用IntlProvider

使用 组件包裹住需要您需要进行语言国际化的组件,用法和React-redux的差不多,当 包裹住某个组件的时候,这个组件本身和组件内部包含的子组件就可以获得所有React-intl提供的接口以及在 中引入的locale配置文件的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from 'react';
import { render } from 'react-dom';
//引入locale配置文件,具体路径根据实际情况填写
import zh_CN from './zh_CN';
import en-US from './en-US';
import zh from 'react-intl/locale-data/zh';
import en from 'react-intl/locale-data/en'
//如果浏览器没有自带intl,则需要在使用npm安装intl之后添加如下代码
import { IntlProvider,addLocaleData } from 'react-intl';
import intl from 'intl';
addLocaleData([...en,...zh]);

...
...

render(
<IntlProvider
locale={'en'}
messages={en_US}
>
<App />
</IntlProvider>,
document.getElementById('container')
);

需要传递两个参数:

locale是传递需要国际化的语言的缩写,通过这个参数可以确定格式化日期,数字,量词的时候按照哪一种语言的规则,这个是规则是intl提供的,一般浏览器会内置这个库,但是在Safari和IE11之前需要自己安装,安装的方法前面已经提及,请自己翻阅。

messages是用于传递刚刚我们在第3步中定义的配置文件的,从示例代码中我们可以看出,首先我们使用Import语句引入了配置文件,然后将配置文件的内容传递给了messages这个参数,此时组件中的所有组件都可以拿到配置文件中的内容了。

5. 使用FormattedMessage

前面的几个步骤其实都是为了这个步骤做铺垫的,在添加了之后,我们就可以在其包裹的包含的所有组件中获取到配置文件的信息,传入组件的id参数也能其在配置文件中对应的字符串了。

使用的方法如下:

1
2
3
4
5
<FormattedMessage
id='hello'
description='say hello to w.'
defaultMessage='Hello, w'
/>

在Js执行的时候,组件就会找到配置文件中,‘hello’键名对应的字符串’Hello, Howard!’.

输出的结果为:

1
<span>Hello, Howard!</span>

那么如何输出含有动态参数的字符串呢?比如Hello,Johnson!,如果我要问候的对象是一个变量呢?

1
2
3
4
5
6
7
8
<FormattedMessage
id='superHello'
description='say hello to w.'
defaultMessage='Hello, {someone}'
values={
someone:this.props.name,
}
/>

以上的例子中,赋给someone的就是一个变量(假设这个变量是通过参数传进这个组件的),注意,如果是这样的话,那么locale配置文件中就要这么写。

superHello:”你好,{ someone } !”